package com.afdor.rws.core.extension;

import com.afdor.rws.annotation.extension.SPI;
import com.afdor.rws.core.extension.factory.DefaultExtensionFactory;
import com.afdor.rws.core.extension.utils.ExtensionUtils;
import lombok.Getter;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 扩展点加载器
 *
 * @author 悭梵
 * @date Created in 2018-09-06 16:32
 */

public class ExtensionLoader<T> {
    /**
     * 扩展点加载版本管理
     */
    private final AtomicInteger loadExtensionVersion = new AtomicInteger(0);
    /**
     * 缓存-扩展服务加载器实例
     * <p>
     * Class 只能是接口类
     */
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>(128);
    /**
     * 获取扩展点加载器要加载类性
     */
    @Getter
    private final Class<T> interfaceClass;
    /**
     * 获取扩展点默认加载名称；可能为空
     * <ul>
     * <li>可通过setDefaultExtensionName修改</li>
     * <li>默认通过SPI获取</li>
     * </ul>
     */
    @Getter
    private volatile String defaultExtensionName;
    /**
     * 扩展点工厂类
     */
    private final ExtensionFactory objectFactory;
    /**
     * 扩展点的类按优先级排序
     */
    private final List<ExtensionItem<T>> orderExtensionClasses = new CopyOnWriteArrayList<ExtensionItem<T>>();
    /**
     * 缓存指定名称对应扩展点的类
     */
    private final ConcurrentMap<String, Class<T>> cachedExtensionClasses = new ConcurrentHashMap<String, Class<T>>(8);

    /**
     * 默认构造方法
     *
     * @param interfaceClass
     */
    ExtensionLoader(Class<T> interfaceClass) {
        this.interfaceClass = interfaceClass;
        if (Objects.equals(ExtensionFactory.class, this.interfaceClass)) {
            this.objectFactory = new DefaultExtensionFactory();
        } else {
            this.objectFactory = ExtensionLoader.getInstance(ExtensionFactory.class).getDefaultExtension();
        }
    }

    /**
     * 返回指定类型加载实例对象
     *
     * @param interfaceClass
     * @param <EL>
     * @return
     */
    public static <EL> ExtensionLoader<EL> getInstance(Class<EL> interfaceClass) {
        if (interfaceClass == null) {
            throw new IllegalArgumentException("Extension type is null");
        }

        ExtensionLoader<EL> loader = (ExtensionLoader<EL>) EXTENSION_LOADERS.get(interfaceClass);
        if (loader == null) {
            EXTENSION_LOADERS.putIfAbsent(interfaceClass, new ExtensionLoader<EL>(interfaceClass));
            loader = (ExtensionLoader<EL>) EXTENSION_LOADERS.get(interfaceClass);
        }
        return loader;
    }

    /**
     * 设置默认扩展点的名称
     *
     * @param defaultExtensionName
     * @return
     */
    public ExtensionLoader<T> setDefaultExtensionName(String defaultExtensionName) {
        if (StringUtils.isBlank(this.defaultExtensionName)) {
            this.defaultExtensionName = defaultExtensionName;
        }
        return this;
    }

    /**
     * 是否存在扩展点
     *
     * @param name
     * @return
     */
    public boolean hasExtension(String name) {
        if (StringUtils.isBlank(name)) {
            throw new NullPointerException("Extension name is not null");
        }
        boolean result = cachedExtensionClasses.containsKey(ExtensionUtils.getFullExtension(interfaceClass, name));
        if (!result) {
            loadExtensionClasses(true);
            result = cachedExtensionClasses.containsKey(ExtensionUtils.getFullExtension(interfaceClass, name));
        }
        return result;
    }

    /**
     * 是否存在指定参数对应构造器的扩展点
     *
     * @param name
     * @param args
     * @return
     */
    public boolean hasConstructor(String name, Object... args) {
        if (StringUtils.isBlank(name)) {
            throw new NullPointerException("Extension name is not null");
        }
        Class<T> clazz = getExtensionClasses(name);
        if (clazz != null) {
            return ExtensionUtils.getExtensionConstructorByParameters(clazz, ClassUtils.toClass(args)) != null;
        }
        return false;
    }

    /**
     * 得到支持的扩展点列表
     *
     * @return
     */
    public Set<String> getSupportedExtensions() {
        if (cachedExtensionClasses.isEmpty()) {
            loadExtensionClasses();
        }
        return Collections.unmodifiableSet(cachedExtensionClasses.keySet());
    }

    /**
     * 返回所有的按优先级排序的扩展点实例，由@Extension.Scope，决定实例作用域
     *
     * @return
     */
    public List<T> getAllExtension() {
        if (cachedExtensionClasses.isEmpty()) {
            loadExtensionClasses();
        }
        return orderExtensionClasses.stream().map(extensionItem -> getExtension(extensionItem.getName())).collect(Collectors.toList());
    }

    /**
     * 返回指定名称的扩展点实例，由@Extension.Scope，决定实例作用域
     *
     * @param name
     * @return
     */
    public T getExtension(String name) {
        if (StringUtils.isBlank(name)) {
            throw new NullPointerException("Extension name is null");
        }
        if (hasExtension(name)) {
            return this.objectFactory.getExtension(interfaceClass, ExtensionUtils.getFullExtension(interfaceClass, name));
        }
        return null;
    }

    /**
     * 返回默认扩展服务， 由@Extension.Scope，决定实例作用域
     *
     * @return
     */
    public T getDefaultExtension() {
        /* 如不存在默认的扩展点配置，则按优先级排序获取最高优先级的扩展点 */
        if (StringUtils.isBlank(defaultExtensionName)) {
            if (interfaceClass.isAnnotationPresent(SPI.class)) {
                return getExtension(StringUtils.trim(interfaceClass.getAnnotation(SPI.class).value()));
            } else {
                if (cachedExtensionClasses.isEmpty()) {
                    loadExtensionClasses();
                }
                /* 取最高优先级的一个扩展点 */
                return getExtension(orderExtensionClasses.get(0).getName());
            }
        }
        /* 存在默认配置，直接获取 */
        else {
            return getExtension(defaultExtensionName);
        }
    }

    /**
     * 返回指定名称的扩展服务，若找不到，则返回默认扩展服务
     *
     * @param name
     * @return
     */
    public T getExtensionOrDefault(String name) {
        if (StringUtils.isBlank(name)) {
            return getDefaultExtension();
        } else {
            if (!hasExtension(name)) {
                return null;
            }
            return getExtension(name);
        }
    }

    /**
     * 返回指定名称的扩展点实例
     *
     * @param name
     * @param args
     * @return
     */
    public T newExtension(String name, Object... args) {
        if (StringUtils.isBlank(name)) {
            throw new NullPointerException("Extension name is null");
        }
        if (hasExtension(name)) {
            return this.objectFactory.newExtension(interfaceClass, ExtensionUtils.getFullExtension(interfaceClass, name), args);
        }
        return null;
    }

    /**
     * 返回默认扩展服务
     *
     * @param args
     * @return
     */
    public T newDefaultExtension(Object... args) {
        /* 如不存在默认的扩展点配置，则按优先级排序获取最高优先级的扩展点 */
        if (StringUtils.isBlank(defaultExtensionName)) {
            if (interfaceClass.isAnnotationPresent(SPI.class)) {
                return newExtension(StringUtils.trim(interfaceClass.getAnnotation(SPI.class).value()));
            } else {
                if (cachedExtensionClasses.isEmpty()) {
                    loadExtensionClasses();
                }
                /* 取最高优先级的一个扩展点 */
                return newExtension(orderExtensionClasses.get(0).getName());
            }
        }
        /* 存在默认配置，直接获取 */
        else {
            return newExtension(defaultExtensionName, args);
        }
    }

    /**
     * 返回指定名称的扩展服务，若找不到，则返回默认扩展服务
     *
     * @param name
     * @param args
     * @return
     */
    public T newExtensionOrDefault(String name, Object... args) {
        if (StringUtils.isBlank(name)) {
            return newDefaultExtension(args);
        } else {
            if (!hasExtension(name)) {
                throw new IllegalArgumentException("the " + interfaceClass.getName() + " named " + name + " is not loaded");
            }
            return newExtension(name, args);
        }
    }

    /**
     * 获取当前扩展类型的列表
     *
     * @return
     */
    private Class<T> getExtensionClasses(String name) {
        if (name == null) {
            throw new NullPointerException("Extension name is null");
        }
        String fullExtensionName = ExtensionUtils.getFullExtension(interfaceClass, name);
        Class<T> clazz = cachedExtensionClasses.get(fullExtensionName);
        if (clazz == null) {
            loadExtensionClasses(true);
            clazz = cachedExtensionClasses.get(fullExtensionName);
            if (clazz == null) {
                throw new NullPointerException("No such extension \"" + name + "\" for " + interfaceClass.getName() + "!");
            }
        }
        return clazz;
    }

    /**
     * 加载扩展点列表
     */
    private void loadExtensionClasses() {
        if (cachedExtensionClasses.isEmpty()) {
            loadExtensionClasses(true);
        }
    }

    /**
     * 加载扩展点列表
     */
    private void loadExtensionClasses(boolean reload) {
        if (interfaceClass == null) {
            throw new NullPointerException("Extension type is null");
        }
        if (reload || cachedExtensionClasses.isEmpty()) {
            // 上锁前的版本号
            int thisLoadExtensionVersion = loadExtensionVersion.get();
            synchronized (this) {
                // 当前版本号与获取锁是的版本号一直，才允许执行加载方法
                if (thisLoadExtensionVersion == loadExtensionVersion.get()) {
                    // 清空并重新加载扩展点信息
                    orderExtensionClasses.clear();
                    cachedExtensionClasses.clear();

                    Optional
                            .ofNullable(this.objectFactory.loadExtensionClasses(interfaceClass))
                            .orElse(Collections.emptyList())
                            .forEach(item -> {
                                Class<T> existsInstanceClass = cachedExtensionClasses.get(item.getName());
                                if (existsInstanceClass != null && !Objects.equals(existsInstanceClass, item.getExtensionClass())) {
                                    throw new IllegalArgumentException("Extension name " + item.getName() + " is exists. exists class " + existsInstanceClass.getName() + ", add class " + item.getExtensionClass().getName());
                                }
                                cachedExtensionClasses.putIfAbsent(item.getName(), item.getExtensionClass());
                            });

                    List<ExtensionItem<T>> classList = new ArrayList<ExtensionItem<T>>(cachedExtensionClasses.size());
                    cachedExtensionClasses.entrySet().forEach(entry -> {
                        classList.add(ExtensionItem.builder().name(entry.getKey()).extensionClass((Class) entry.getValue()).build());
                    });
                    /* 优先级排序 */
                    ExtensionUtils.orderExtensionByClass(classList);
                    orderExtensionClasses.addAll(classList);
                    // 版本号递增
                    loadExtensionVersion.incrementAndGet();
                }
            }
        }
    }

}
